package me.ingxin.android.router.compiler.processor

import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.TypeSpec
import me.ingxin.android.router.annotaions.Component
import me.ingxin.android.router.annotaions.compiler.LComponent
import me.ingxin.android.router.compiler.util.*
import java.util.*
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.util.ElementFilter

/**
 * Created by ingxin on 2022/4/15
 */
class ComponentProcessor(
    processorUtil: ProcessorUtil,
    logger: Logger
) : BaseProcessor(processorUtil, logger) {

    override fun getSupportedAnnotationTypes(): MutableSet<String> {
        return HashSet<String>().apply { add(Component::class.java.name) }
    }

    /**
     * 解析[Component]并生成需要处理的文件
     */
    fun process(env: RoundEnvironment) {
        val componentElement = findComponentElement(env) ?: return
        writeIndex(componentElement)
    }

    /**
     * 解析[Component]生成[LComponent]
     */
    fun getComponent(env: RoundEnvironment): LComponent? {
        val element = findComponentElement(env) ?: return null
        return getComponentAnnotation(element).let {
            LComponent(
                it.name,
                element.qualifiedName.toString(),
                it.priority,
                it.dependencies
            )
        }
    }

    /**
     * 检测并获取目标类上的[Component]注解
     * @param element 使用注解的类元素
     */
    private fun getComponentAnnotation(element: TypeElement): Component {
        val annotation = element.getAnnotation(Component::class.java)
        val componentName = annotation.name

        if (!element.modifiers.contains(Modifier.PUBLIC)) {
            logger.throwException("[${element.qualifiedName}] modifier must be public!")
        }

        if (componentName.contains(" ")) {
            logger.throwException("[${element.qualifiedName}] error, annotation [@${Component::class.java.simpleName}] name contains blank")
        }
        val tempMap = HashMap<String, Boolean>()
        for (item in annotation.dependencies) {
            if (tempMap[item] == true) {
                logger.throwException("[${element.qualifiedName}], [@${Component::class.java.simpleName}] error, dependencies[] duplicate value [$item]")
            }
            if (item.contains(" ")) {
                logger.throwException("[${element.qualifiedName}], [@${Component::class.java.simpleName}] error, dependencies[] contains blank")
            }
            tempMap[item] = true
        }
        tempMap.clear()
        return annotation
    }

    /**
     * 找到使用[Component]注解的类
     */
    private fun findComponentElement(env: RoundEnvironment): TypeElement? {
        val annotatedElements = env.getElementsAnnotatedWith(Component::class.java) ?: return null
        val list = ElementFilter.typesIn(annotatedElements)?.toList() ?: return null
        if (list.isEmpty()) {
            return null
        }
        //一个module模块只能包含一个@Component注解
        if (list.size > 1) {
            val a = list[0]
            val b = list[1]
            logger.throwException("Duplicate annotation [@${Component::class.java.simpleName}] found in class [${a.qualifiedName}] and class [${b.qualifiedName}]")
        }

        val componentElement = list[0]
        val typeComponent =
            processorUtil.env.elementUtils.getTypeElement(COMPONENT_NAME).asType()
        if (!processorUtil.env.typeUtils.isSubtype(componentElement.asType(), typeComponent)) {
            //注解只能用在Component子类
            logger.throwException("[${componentElement.qualifiedName}] error, [@${Component::class.java.simpleName}] annotation must be used at [${COMPONENT_NAME}] or subtype!")
        }
        return componentElement
    }

    private fun writeIndex(componentElement: TypeElement) {
        //使用Component注解的类名
        val clazzName = componentElement.qualifiedName.toString().replace(".", "_")
        val indexClassName =
            if (clazzName.length > (MAX_FILE_NAME_LENGTH - COMPONENT_INDEX_NAME_PREFIX.length)) {
                "${COMPONENT_INDEX_NAME_PREFIX}${
                    UUID.nameUUIDFromBytes(clazzName.toByteArray()).toString().replace("-", "_")
                }"
            } else {
                "${COMPONENT_INDEX_NAME_PREFIX}$clazzName"
            }

        //Component信息
        val annotation = getComponentAnnotation(componentElement)
        val componentName = annotation.name

        val abb = CodeBlock.builder()
        abb.add(
            """|
               |····name·=·"$componentName",
               |····destination·=·"${componentElement.qualifiedName}",
               |····priority·=·${annotation.priority},
               |····dependencies·=·[""".trimMargin()
        )
        for ((index, item) in annotation.dependencies.withIndex()) {
            abb.add("\"$item\"")
            if (index != annotation.dependencies.size - 1) {
                abb.add(",")
            }
        }
        abb.add("]\n")

        //生成注解
        val annotationSpec = AnnotationSpec.builder(LComponent::class)
            .addMember(abb.build())
            .build()
        val indexSpec = TypeSpec.classBuilder(indexClassName)
            .addAnnotation(annotationSpec)
            .build()

        //创建类文件
        processorUtil.writeClass(COMPONENT_INDEX_PACKAGE_NAME, indexSpec)

    }

}